import json
from pathlib import Path
from typing import Dict, List, Any
import logging
import docker
import yaml
import subprocess
import shutil
from datetime import datetime

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("Deployment")

class ProductionDeployer:
    """生产环境部署管理器"""
    def __init__(self, config: Dict[str, Any]):
        self.config = config
        self.deploy_dir = Path("b/deployment")
        self.deploy_dir.mkdir(parents=True, exist_ok=True)
        self.docker_client = docker.from_env()
        self._validate_config()
        
    def _validate_config(self):
        """验证部署配置"""
        required = ["app_name", "version", "services", "infrastructure"]
        for field in required:
            if field not in self.config:
                raise ValueError(f"缺少必要配置项: {field}")
                
        logger.info(f"验证部署配置完成，准备部署 {self.config['app_name']} v{self.config['version']}")
    
    def build_docker_images(self):
        """构建Docker镜像"""
        logger.info("开始构建Docker镜像...")
        
        # 创建Dockerfile
        self._create_dockerfile()
        
        # 构建基础镜像
        base_image = f"{self.config['app_name']}-base:{self.config['version']}"
        self._build_image(
            dockerfile=str(self.deploy_dir / "Dockerfile.base"),
            tag=base_image,
            build_args={
                "PYTHON_VERSION": "3.9",
                "APP_DIR": "/app"
            }
        )
        
        # 构建服务镜像
        service_images = {}
        for service in self.config["services"]:
            image_name = f"{self.config['app_name']}-{service['name']}:{self.config['version']}"
            self._build_service_image(service, base_image, image_name)
            service_images[service["name"]] = image_name
            
        logger.info("所有Docker镜像构建完成")
        return service_images
    
    def _create_dockerfile(self):
        """创建Dockerfile"""
        # 基础Dockerfile
        base_dockerfile = self.deploy_dir / "Dockerfile.base"
        if not base_dockerfile.exists():
            base_dockerfile.write_text("""
FROM python:${PYTHON_VERSION}-slim

WORKDIR ${APP_DIR}

RUN apt-get update && apt-get install -y \
    gcc \
    python3-dev \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["python", "system_integration.py"]
""")
        
        # 服务Dockerfile模板
        service_dockerfile = self.deploy_dir / "Dockerfile.service"
        if not service_dockerfile.exists():
            service_dockerfile.write_text("""
FROM ${BASE_IMAGE}

ENV SERVICE_NAME=${SERVICE_NAME}

COPY b/${SERVICE_NAME} /app/b/${SERVICE_NAME}

CMD ["python", "b/${SERVICE_NAME}/main.py"]
""")
    
    def _build_image(self, dockerfile: str, tag: str, build_args: Dict[str, str] = None):
        """构建单个Docker镜像"""
        logger.info(f"构建镜像 {tag}...")
        
        try:
            image, logs = self.docker_client.images.build(
                path=".",
                dockerfile=dockerfile,
                tag=tag,
                buildargs=build_args,
                rm=True
            )
            
            for log in logs:
                if "stream" in log:
                    logger.debug(log["stream"].strip())
                    
            logger.info(f"镜像 {tag} 构建成功")
            return image
            
        except docker.errors.BuildError as e:
            logger.error(f"构建失败: {str(e)}")
            for log in e.build_log:
                if "stream" in log:
                    logger.error(log["stream"].strip())
            raise
            
    def _build_service_image(self, service: Dict[str, Any], base_image: str, image_name: str):
        """构建服务镜像"""
        service_dir = self.deploy_dir / "tmp" / service["name"]
        service_dir.mkdir(parents=True, exist_ok=True)
        
        # 创建服务Dockerfile
        dockerfile_content = Path("b/deployment/Dockerfile.service").read_text()
        dockerfile_content = dockerfile_content.replace("${BASE_IMAGE}", base_image)
        dockerfile_content = dockerfile_content.replace("${SERVICE_NAME}", service["name"])
        
        (service_dir / "Dockerfile").write_text(dockerfile_content)
        
        # 构建服务镜像
        self._build_image(
            dockerfile=str(service_dir / "Dockerfile"),
            tag=image_name
        )
        
        # 清理临时目录
        shutil.rmtree(service_dir.parent)
    
    def generate_kubernetes_configs(self, service_images: Dict[str, str]):
        """生成Kubernetes配置"""
        logger.info("生成Kubernetes部署配置...")
        
        # 创建命名空间配置
        namespace = {
            "apiVersion": "v1",
            "kind": "Namespace",
            "metadata": {
                "name": self.config["app_name"]
            }
        }
        
        # 创建服务配置
        services = []
        deployments = []
        
        for service in self.config["services"]:
            # Service配置
            svc = {
                "apiVersion": "v1",
                "kind": "Service",
                "metadata": {
                    "name": service["name"],
                    "namespace": self.config["app_name"],
                    "labels": {
                        "app": self.config["app_name"],
                        "service": service["name"]
                    }
                },
                "spec": {
                    "ports": [{
                        "port": service["port"],
                        "targetPort": service["port"]
                    }],
                    "selector": {
                        "app": self.config["app_name"],
                        "service": service["name"]
                    }
                }
            }
            services.append(svc)
            
            # Deployment配置
            deploy = {
                "apiVersion": "apps/v1",
                "kind": "Deployment",
                "metadata": {
                    "name": f"{service['name']}-deployment",
                    "namespace": self.config["app_name"],
                    "labels": {
                        "app": self.config["app_name"],
                        "service": service["name"]
                    }
                },
                "spec": {
                    "replicas": service.get("replicas", 2),
                    "selector": {
                        "matchLabels": {
                            "app": self.config["app_name"],
                            "service": service["name"]
                        }
                    },
                    "template": {
                        "metadata": {
                            "labels": {
                                "app": self.config["app_name"],
                                "service": service["name"]
                            }
                        },
                        "spec": {
                            "containers": [{
                                "name": service["name"],
                                "image": service_images[service["name"]],
                                "ports": [{
                                    "containerPort": service["port"]
                                }],
                                "env": self._get_env_vars(service),
                                "resources": {
                                    "requests": {
                                        "cpu": service.get("cpu_request", "100m"),
                                        "memory": service.get("mem_request", "256Mi")
                                    },
                                    "limits": {
                                        "cpu": service.get("cpu_limit", "500m"),
                                        "memory": service.get("mem_limit", "1Gi")
                                    }
                                },
                                "livenessProbe": {
                                    "httpGet": {
                                        "path": "/health",
                                        "port": service["port"]
                                    },
                                    "initialDelaySeconds": 30,
                                    "periodSeconds": 10
                                },
                                "readinessProbe": {
                                    "httpGet": {
                                        "path": "/ready",
                                        "port": service["port"]
                                    },
                                    "initialDelaySeconds": 5,
                                    "periodSeconds": 5
                                }
                            }],
                            "imagePullPolicy": "IfNotPresent"
                        }
                    }
                }
            }
            deployments.append(deploy)
        
        # 创建ConfigMap
        configmap = {
            "apiVersion": "v1",
            "kind": "ConfigMap",
            "metadata": {
                "name": f"{self.config['app_name']}-config",
                "namespace": self.config["app_name"]
            },
            "data": {
                "APP_NAME": self.config["app_name"],
                "APP_VERSION": self.config["version"]
            }
        }
        
        # 保存配置
        k8s_dir = self.deploy_dir / "kubernetes"
        k8s_dir.mkdir(exist_ok=True)
        
        with open(k8s_dir / "namespace.yaml", "w") as f:
            yaml.dump(namespace, f)
            
        with open(k8s_dir / "services.yaml", "w") as f:
            yaml.dump_all(services, f)
            
        with open(k8s_dir / "deployments.yaml", "w") as f:
            yaml.dump_all(deployments, f)
            
        with open(k8s_dir / "configmap.yaml", "w") as f:
            yaml.dump(configmap, f)
            
        logger.info(f"Kubernetes配置已保存到 {k8s_dir}")
        
        return {
            "namespace": namespace,
            "services": services,
            "deployments": deployments,
            "configmap": configmap
        }
    
    def _get_env_vars(self, service: Dict[str, Any]) -> List[Dict[str, str]]:
        """获取环境变量配置"""
        env_vars = [
            {"name": "SERVICE_NAME", "value": service["name"]},
            {"name": "LOG_LEVEL", "value": "INFO"}
        ]
        
        if "kafka" in service["name"]:
            env_vars.extend([
                {"name": "KAFKA_BROKERS", "value": "kafka:9092"},
                {"name": "KAFKA_TOPIC", "value": "career-events"}
            ])
            
        if "ray" in service["name"]:
            env_vars.extend([
                {"name": "RAY_ADDRESS", "value": "ray-head:6379"},
                {"name": "REDIS_PASSWORD", "value": "password123"}
            ])
            
        return env_vars
    
    def deploy_to_kubernetes(self):
        """部署到Kubernetes集群"""
        logger.info("开始部署到Kubernetes集群...")
        
        # 构建镜像
        service_images = self.build_docker_images()
        
        # 生成配置
        k8s_configs = self.generate_kubernetes_configs(service_images)
        
        # 应用配置
        self._apply_kubernetes_configs()
        
        # 等待服务就绪
        self._wait_for_services()
        
        logger.info("Kubernetes部署完成")
    
    def _apply_kubernetes_configs(self):
        """应用Kubernetes配置"""
        k8s_dir = self.deploy_dir / "kubernetes"
        
        # 按顺序应用配置
        config_order = [
            "namespace.yaml",
            "configmap.yaml",
            "services.yaml",
            "deployments.yaml"
        ]
        
        for config in config_order:
            config_file = k8s_dir / config
            subprocess.run([
                "kubectl", "apply", "-f", str(config_file)
            ], check=True)
            logger.info(f"已应用 {config}")
    
    def _wait_for_services(self, timeout: int = 300):
        """等待服务就绪"""
        logger.info("等待服务就绪...")
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            ready = True
            
            # 检查部署状态
            result = subprocess.run([
                "kubectl", "get", "deployments", 
                "-n", self.config["app_name"],
                "-o", "json"
            ], capture_output=True, text=True)
            
            if result.returncode == 0:
                deployments = json.loads(result.stdout)
                for item in deployments["items"]:
                    if item["status"].get("readyReplicas", 0) < item["spec"]["replicas"]:
                        ready = False
                        break
                        
                if ready:
                    logger.info("所有服务已就绪")
                    return
                    
            time.sleep(5)
            
        raise TimeoutError("等待服务就绪超时")
    
    def setup_infrastructure(self):
        """设置基础设施"""
        logger.info("设置基础设施...")
        
        # 使用Terraform创建资源
        self._run_terraform()
        
        # 配置网络
        self._configure_network()
        
        logger.info("基础设施设置完成")
    
    def _run_terraform(self):
        """运行Terraform"""
        tf_dir = self.deploy_dir / "terraform"
        tf_dir.mkdir(exist_ok=True)
        
        # 创建main.tf (示例)
        main_tf = tf_dir / "main.tf"
        if not main_tf.exists():
            main_tf.write_text("""
provider "aws" {
  region = "us-east-1"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"
}
""")
        
        # 初始化Terraform
        subprocess.run(["terraform", "init"], cwd=tf_dir, check=True)
        
        # 应用配置
        subprocess.run(["terraform", "apply", "-auto-approve"], cwd=tf_dir, check=True)
        
        logger.info("Terraform执行完成")
    
    def _configure_network(self):
        """配置网络"""
        # 示例: 创建网络策略
        network_policy = {
            "apiVersion": "networking.k8s.io/v1",
            "kind": "NetworkPolicy",
            "metadata": {
                "name": "allow-service-communication",
                "namespace": self.config["app_name"]
            },
            "spec": {
                "podSelector": {
                    "matchLabels": {
                        "app": self.config["app_name"]
                    }
                },
                "ingress": [{
                    "from": [{
                        "podSelector": {
                            "matchLabels": {
                                "app": self.config["app_name"]
                            }
                        }
                    }]
                }]
            }
        }
        
        policy_file = self.deploy_dir / "kubernetes" / "network-policy.yaml"
        with open(policy_file, "w") as f:
            yaml.dump(network_policy, f)
            
        subprocess.run([
            "kubectl", "apply", "-f", str(policy_file)
        ], check=True)
        
        logger.info("网络策略已配置")

if __name__ == "__main__":
    # 示例部署配置
    config = {
        "app_name": "career-system",
        "version": "1.0.0",
        "services": [
            {
                "name": "api",
                "port": 8000,
                "replicas": 3,
                "cpu_request": "200m",
                "mem_request": "512Mi"
            },
            {
                "name": "kafka-processor",
                "port": 8080,
                "replicas": 2
            },
            {
                "name": "ray-worker",
                "port": 8265,
                "replicas": 4
            }
        ],
        "infrastructure": {
            "cloud_provider": "aws",
            "region": "us-east-1",
            "k8s_version": "1.21"
        }
    }
    
    # 创建并执行部署
    deployer = ProductionDeployer(config)
    
    try:
        # 设置基础设施
        deployer.setup_infrastructure()
        
        # 部署到Kubernetes
        deployer.deploy_to_kubernetes()
        
        print("\n部署成功完成！")
        print(f"应用名称: {config['app_name']}")
        print(f"版本: {config['version']}")
        print(f"服务数量: {len(config['services'])}")
        
    except Exception as e:
        logger.error(f"部署失败: {str(e)}")
        raise